package com.gopay.common.cps.service.autosettle.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.gopay.common.constants.autosettle.WorkDayEnum;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.route.PayRouteConstants;
import com.gopay.common.constants.solution.SolutionTypeConstant;
import com.gopay.common.constants.trans.PayChannel;
import com.gopay.common.cps.dao.autosettle.PayRouteLogDAO;
import com.gopay.common.cps.service.autosettle.impl.AutoSettleHelper;
import com.gopay.common.domain.bank.route.po.pay.PayRouteLog;
import com.gopay.common.domain.cps.CpsGenMainOrder;
import com.gopay.common.order.dao.CommonQueryDAO;
import com.gopay.common.util.DateUtils;
import com.gopay.common.util.FastJsonUtil;
import com.gopay.common.util.StringTools;

/**
 * @description 付款路由助手类
 * @author jianghp
 * @date 2018/4/17 10:19
 * Copyright (c) 2018. www.gopay.com.cn Inc.All rights reserved.
 */
@Service(value = "payRouteHelper")
public class PayRouteHelper {
	
	private static final Logger logger = LoggerFactory.getLogger(PayRouteHelper.class);

    @Resource(name = "commonQueryDAO")
    private CommonQueryDAO commonQueryDAO;
    
    @Resource(name = "autoSettleHelper")
    private AutoSettleHelper autoSettleHelper;

	@Resource(name = "payRouteLogDAO")
	private PayRouteLogDAO payRouteLogDAO;

	// E键支付 默认可自动结算银行
	private final String E_PAY_BANKS="BOC,ABC,ICBC,CCB,ICBC_SUPER,ICBC_CROSS,NUCCPAY";

	/**
	 * 根据银行名称匹配银行代码 如果都匹配不到返回others 代表其他银行
	 * @param gopayOrderId
	 * @param bankName
	 * @return
	 */
	public String switchBankCodeByName(String gopayOrderId,String bankName){
    	logger.info("订单号：{}，根据收款银行名称：{} --》转换成银行代码，开始",gopayOrderId,bankName);
    	if(StringUtils.isBlank(bankName)){
    		return null;
    	}
    	if(bankName.indexOf("工行")!=-1 || bankName.indexOf("工商")!=-1){
    		return BankConstants.BANK_LOGO_ICBC;
    	}else if(bankName.indexOf("招行")!=-1 || bankName.indexOf("招商")!=-1){
			return BankConstants.BANK_LOGO_CMB;
    	}else if(bankName.indexOf("农行")!=-1 || bankName.indexOf("农业银行")!=-1){
			return BankConstants.BANK_LOGO_ABC;
    	}else if(bankName.indexOf("中行")!=-1 || bankName.indexOf("中国银行")!=-1){
			return BankConstants.BANK_LOGO_BOC;
    	}else if(bankName.indexOf("建行")!=-1 || bankName.indexOf("建设银行")!=-1){
			return BankConstants.BANK_LOGO_CCB;
    	}

		StringBuilder sb = new StringBuilder();
		sb.append(" select t.bank_code from bis_bank_info_main t where t.is_del = 0 and t.enable = 1 and t.bank_name =:bankName");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("bankName", bankName);
		List<Object[]> rsList = commonQueryDAO.findBySql(sb.toString(), map);
		if (rsList != null && rsList.size() > 0) {
			return String.valueOf(rsList.get(0));
		}else{
			return BankConstants.BANK_LOGO_OTHERS;
		}
    }

	/**
	 * 获取所有可用的自动结算支持的银行
	 * @param mainOrder
	 * @param acctId
	 * @return
	 */
	public String queryPayBankCodeByAutoSettle(CpsGenMainOrder mainOrder,String acctId){
		logger.info("获取可用的自动结算方案支持的银行，付款单：{}，付款账号为{}",mainOrder.getGopayOrderId(),acctId);
		String banks = null;
		// 当前时间
        Date curDate = new Date();
		Boolean isWorkDay = true;
    	if (autoSettleHelper.isHoliday(curDate)) {
			isWorkDay = false;
        }
		if(isPersonAcct(acctId)){
			//付款账户为个人，先走个性化，个性化没有再走通用
			if(isIndividualizationAutoSettle(acctId)){
				logger.info("付款单：{}，付款账户为个人，走个性化自动结算方案",mainOrder.getGopayOrderId());
				//判断是否走个性化
				banks = queryPayBankCode(acctId, mainOrder.getGopayIntTxnCd(),isWorkDay);
			}else{
				logger.info("付款单：{}，付款账户为个人，走通用自动结算方案",mainOrder.getGopayOrderId());
				//根据账户查询产品线code
				String procCode=queryProcCode(acctId);
				if(StringUtils.isBlank(procCode)){
					return null;
				}
				banks = queryPayBankCode(procCode, mainOrder.getGopayIntTxnCd(),isWorkDay);
			}
		}else{
    		if(StringUtils.equals(mainOrder.getRemark3(), PayChannel._35.value)||StringUtils.equals(mainOrder.getRemark3(), PayChannel._36.value)){
				banks = filterBankCodes(E_PAY_BANKS);
			}else {
				logger.info("付款单：{}，付款账户为企业，走个性化自动结算方案",mainOrder.getGopayOrderId());
				//付款商户为企业，只走个性化
				banks = queryPayBankCode(acctId, mainOrder.getGopayIntTxnCd(),isWorkDay);
			}
		}
    	return banks;
    }


	/**
	 * 根据账户号获取账户类型
	 * @param acctId
	 * @return
	 */
	private String queryProcCode(String acctId) {
		StringBuilder sql = new StringBuilder();
		sql.append("select t.prod_code, t.acct_id ");
		sql.append("  from acps_acct_mast t    ");
		sql.append(" where t.acct_id = :acctId ");//根据acctId可以唯一确定
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("acctId", acctId);
		List<Object[]> rsList = commonQueryDAO.findBySql(sql.toString(), map);
		if (rsList != null && rsList.size() > 0 ) {
			return (String) rsList.get(0)[0];
		}
		return null;
	}

	/**
	 * 判断是否个性化过自动结算方案
	 */
	public boolean isIndividualizationAutoSettle(String acctId) {
		StringBuilder sql = new StringBuilder();
		sql.append("select t.acct_id, t.solution_id                ");
		sql.append("        from ss_acct_solution_rel t            ");
		sql.append("       where t.acct_id =:acctId                ");
		sql.append("         and t.solution_type =:solutionType   ");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("acctId", acctId);
		map.put("solutionType", SolutionTypeConstant.TRANS_SETTLE);
		List<Object[]> ssRelAcct = commonQueryDAO.findBySql(sql.toString(), map);
		if (ssRelAcct != null && ssRelAcct.size() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * 查询付款客户对应的自动结算银行，并过滤未开通的直连银行
	 * @param acctId
	 * @param gopayIntTxnCd
	 * @param isWorkDay
	 * @return
	 */
	private String queryPayBankCode(String acctId,String gopayIntTxnCd,boolean isWorkDay) {
		StringBuilder sb = new StringBuilder();
		sb.append(" select t.gopay_int_txn_cd, t.acct_id, TO_CHAR(WMSYS.WM_CONCAT(t.bank_code)) bank_code ");
		sb.append(" from SS_TRANS_SETTLE_DTL t");
		sb.append(" where t.gopay_int_txn_cd = :intTxnCd ");
		sb.append("  and t.acct_id = :acctId ");
		sb.append("  and t.is_work_day =:isWorkDay");
		sb.append(" group by t.gopay_int_txn_cd,t.acct_id ");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("intTxnCd", gopayIntTxnCd);
		map.put("acctId", acctId);
		map.put("isWorkDay", isWorkDay ? WorkDayEnum.WorkDay.key: WorkDayEnum.Holiday.key);
		List<Object[]> rsList = commonQueryDAO.findBySql(sb.toString(), map);
		if (rsList != null && rsList.size() > 0 && rsList.get(0).length == 3) {
			//若银行对应的自动结算状态为“关闭”，则在汇路选择时，不能选取该银行作为付款汇路。
			return filterBankCodes((String) rsList.get(0)[2]);
		}
		return null;
	}

    /**
     * 过滤掉AS_BANK_LIMIT_CONFIG 中自动结算状态关闭的BankCode 0代表关闭
     * 若BankCode不在AS_BANK_LIMIT_CONFIG中则不处理，例如TBANK不做处理
     * @param bankCodes
     * @return
     */
	private String filterBankCodes(String bankCodes){
        if (bankCodes != null) {
            Map<String, String> map = getAutoSettleBanks();
            List<String> tempList = Arrays.asList(bankCodes.split(","));
            //将Arrays.ArrayList类型转为ArrayList
            List<String> bankList = new ArrayList<>(tempList);
            for (Map.Entry<String,String> k : map.entrySet()) {
                if (bankList.contains(k.getKey()) && "0".equals(k.getValue())) {
                    bankList.remove(k.getKey());
                }
            }
            return StringUtils.join(bankList, ",");
        } else {
            return bankCodes;
        }

    }

	/**
	 * 获取AS_BANK_LIMIT_CONFIG 表中银行代码与其自动结算开关状态
	 * @return map(BANK_CODE,SETTLE_STATUS)
	 */
	private Map<String,String> getAutoSettleBanks(){
        HashMap<String, String> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        sb.append(" SELECT conf.BANK_CODE, conf.SETTLE_STATUS ");
        sb.append(" FROM AS_BANK_LIMIT_CONFIG conf ");
        sb.append(" where 1 = 1 ");
        List<Object[]> rsList = commonQueryDAO.findBySql(sb.toString());
        if (rsList != null && rsList.size() > 0) {
            for (Object[] x: rsList) {
                map.put(StringTools.checkNulltoString(x[0]),StringTools.checkNulltoString(x[1]));
            }
        }
        return map;
    }

	/**
	 * 判断客户是否是个人账户
	 * @param acctId
	 * @return
	 */
	private Boolean isPersonAcct(String acctId) {
		if (StringUtils.isNotBlank(acctId)&&StringUtils.equals("1", acctId.substring(9, 10))) {
			// 个人账户
			return true;
		}
		return false;
	}

	/**
	 * 使用工行超级网银的判断
	 * @param mainOrder
	 * @param bankName
	 * @param bcFlag
	 * @param cardNum
	 * @return
	 */
	public boolean useSuper(CpsGenMainOrder mainOrder,String bankName,String bcFlag,String cardNum){
		if(autoSettleHelper.isBankAcross()){
			Boolean	superFlag = autoSettleHelper.existCorrespondBank(bankName, cardNum);
			logger.info("工行超级网银校验，orderId:{},isPrivate:{},bankName is ICBC super:{}",new String[]{mainOrder.getGopayOrderId(),autoSettleHelper.isPrivate(bcFlag).toString(),superFlag.toString()});
			return superFlag;
		}
		return false;
	}

	/**
	 * 用工行大额的校验
	 * @param mainOrder
	 * @param bankName
	 * @param bcFlag
	 * @param cardNum
	 * @return
	 */
	public boolean useCross(CpsGenMainOrder mainOrder,String bankName,String bcFlag,String cardNum){
		if(autoSettleHelper.isBankAcross()){
			Boolean crossFlag = autoSettleHelper.existCorrespondBankCross(bankName, cardNum);
			logger.info("工行大额校验，orderId:{},isPrivate:{},bankName is ICBC cross:{}",new String[]{mainOrder.getGopayOrderId(),autoSettleHelper.isPrivate(bcFlag).toString(),crossFlag.toString()});
			return crossFlag;
		}
		return false;
	}

	/**
	 * 获取商户付款路由
	 * @param mainOrder
	 * @param bcFlag
	 * @param splitFlag
	 * @param receiveBankCode
	 * @return
	 */
	public List<String> chooseSpecialRoute(CpsGenMainOrder mainOrder, String bcFlag, String splitFlag, String receiveBankCode,String custId) {
		Date curDate = new Date();
		String sysdate = DateUtils.getCurrDate("HH:mm:ss");
		StringBuilder sql = new StringBuilder();
		sql.append(" select distinct rr.inter_channel,rr.bak_channel,rsc.cust_id,rs.solution_id,rs.solution_name,sr.rule_id,rr.rule_name,sr.priority,rrb.bank_code ");
		sql.append(" from pay_spec_route_solution rs ");
		sql.append(" left join pay_spec_route_solu_cust_rel rsc on rs.solution_id = rsc.solution_id ");
		sql.append(" left join pay_spec_solution_rule_rel sr on rs.solution_id = sr.solution_id  ");
		sql.append(" left join pay_spec_route_rule rr on rr.id = sr.rule_id  ");
		sql.append(" left join pay_spec_route_rule_bank_rel rrb on rrb.rule_id = rr.id  ");
		sql.append(" where rs.is_valid='1' and rsc.del_flag='2' and sr.del_flag='2' and rr.is_valid='1' and rr.del_flag='2' and rrb.del_flag='2' ");

		sql.append(" and rsc.cust_id =:custId and rrb.bank_code=:receiveBankCode " +
				"   and (rr.pay_product=:gopayIntTxnCd or rr.pay_product = '0') " +
				"   and (rr.min_amt < :settleAmt and rr.max_amt >= :settleAmt ) " +
				"   and (rr.bp_flag=:bcFlag or rr.bp_flag = '0') " +
				"   and (rr.split_bill_flag=:splitFlag or rr.split_bill_flag = '0') " +
				"   and (rr.wh_flag=:whFlag or rr.wh_flag = '0') " +
				"   and (rr.start_time <= :curDate and rr.end_time >= :curDate ) ");

		sql.append(" order by sr.priority asc ");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("custId", custId);
		map.put("receiveBankCode", receiveBankCode);
		map.put("gopayIntTxnCd", mainOrder.getGopayIntTxnCd());
		map.put("settleAmt", mainOrder.getSettleAmt());
		map.put("bcFlag", bcFlag);
		map.put("splitFlag", PayRouteConstants.SplitBillEnum.YES.getFlag().equals(splitFlag)?splitFlag: PayRouteConstants.SplitBillEnum.NO.getFlag());
		map.put("whFlag", autoSettleHelper.isHoliday(curDate) ? PayRouteConstants.WeekdayHolidayEnum.HOLIDAY.getFlag():PayRouteConstants.WeekdayHolidayEnum.WEEKDAY.getFlag());
		map.put("curDate", sysdate);
		List<Object[]> specials = commonQueryDAO.findBySql(sql.toString(), map);
		logger.info("付款单：{}，在商户路由方案中共找到{}条规则,查询条件为：{}",new String[]{mainOrder.getGopayOrderId(),String.valueOf(specials.size()),FastJsonUtil.toJSONString(map)});
		if (specials != null && specials.size() > 0) {
			Object[] special = specials.get(0);
			logger.info("付款单：{}，在商户路由方案中找到优先级最高的规则为:{}",mainOrder.getGopayOrderId(), FastJsonUtil.toJSONString(special));
			List<String> banks = new ArrayList<>();
			if(special[0]!=null){
				banks.add(special[0].toString());
			}
			if(special[1]!=null){
				banks.add(special[1].toString());
			}
			try {
				PayRouteLog oldLog = payRouteLogDAO.find(PayRouteLog.class, mainOrder.getGopayOrderId());
				if(oldLog == null){
					PayRouteLog payLog = new PayRouteLog();
					payLog.setGopayOrderId(mainOrder.getGopayOrderId());
					payLog.setType("1");
					if(special[3]!=null){
						payLog.setSolutionId(Long.valueOf(special[3].toString()));
					}
					if(special[5]!=null){
						payLog.setRuleId(Long.valueOf(special[5].toString()));
					}
					payLog.setPayReq(FastJsonUtil.toJSONString(map));
					payLog.setPayRep(FastJsonUtil.toJSONString(special));
					payLog.setCreatedTs(curDate);
					payRouteLogDAO.save(payLog);
				}
			}catch (Exception e){
				logger.error("付款单：{},保存付款路由日志异常",mainOrder.getGopayOrderId(),e);
			}
			return banks;
		}
		return null;
	}

	/**
	 * 获取通用付款路由
	 * @param mainOrder
	 * @param bcFlag
	 * @param splitFlag
	 * @param receiveBankCode
	 * @return
	 */
	public List<String> chooseCommonRoute(CpsGenMainOrder mainOrder, String bcFlag, String splitFlag, String receiveBankCode) {
		Date curDate = new Date();
		String sysdate = DateUtils.getCurrDate("HH:mm:ss");
		StringBuilder sql = new StringBuilder();
		sql.append(" select distinct rr.inter_channel,rr.bak_channel,rs.solution_id,rs.solution_name,sr.rule_id,rr.rule_name,sr.priority,rrb.bank_code ");
		sql.append(" from pay_normal_route_solution rs ");
		sql.append(" left join pay_normal_route_solu_rule_rel sr on rs.solution_id = sr.solution_id  ");
		sql.append(" left join pay_normal_route_rule rr on rr.id = sr.rule_id  ");
		sql.append(" left join pay_normal_route_rule_bank_rel rrb on rrb.rule_id = rr.id  ");
		sql.append(" where rs.is_valid='1' and sr.del_flag='2' and rr.is_valid='1' and rr.del_flag='2' and rrb.del_flag='2' ");

		sql.append(" and rrb.bank_code=:receiveBankCode " +
				"   and (rr.pay_product=:gopayIntTxnCd or rr.pay_product = '0') " +
				"   and (rr.min_amt < :settleAmt and rr.max_amt >= :settleAmt ) " +
				"   and (rr.bp_flag=:bcFlag or rr.bp_flag = '0') " +
				"   and (rr.split_bill_flag=:splitFlag or rr.split_bill_flag = '0') " +
				"   and (rr.wh_flag=:whFlag or rr.wh_flag = '0') " +
				"   and (rr.start_time <= :curDate and rr.end_time >= :curDate ) ");

		sql.append(" order by sr.priority asc ");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("receiveBankCode", receiveBankCode);
		map.put("gopayIntTxnCd", mainOrder.getGopayIntTxnCd());
		map.put("settleAmt", mainOrder.getSettleAmt());
		map.put("bcFlag", bcFlag);
		map.put("splitFlag", PayRouteConstants.SplitBillEnum.YES.getFlag().equals(splitFlag)?splitFlag: PayRouteConstants.SplitBillEnum.NO.getFlag());
		map.put("whFlag", autoSettleHelper.isHoliday(curDate) ? PayRouteConstants.WeekdayHolidayEnum.HOLIDAY.getFlag():PayRouteConstants.WeekdayHolidayEnum.WEEKDAY.getFlag());
		map.put("curDate", sysdate);

		List<Object[]> commons = commonQueryDAO.findBySql(sql.toString(), map);
		logger.info("付款单：{}，在通用路由方案中共找到{}条规则，查询条件为：{}",new String[]{mainOrder.getGopayOrderId(),String.valueOf(commons.size()),FastJsonUtil.toJSONString(map)});
		if (commons != null && commons.size() > 0) {
			Object[] common = commons.get(0);
			logger.info("付款单：{}，在通用路由方案中找到优先级最高的规则为:{}",mainOrder.getGopayOrderId(), FastJsonUtil.toJSONString(common));
			List<String> banks = new ArrayList<>();
			if(common[0]!=null){
				banks.add(common[0].toString());
			}
			if(common[1]!=null){
				banks.add(common[1].toString());
			}
			try {
				PayRouteLog oldLog = payRouteLogDAO.find(PayRouteLog.class, mainOrder.getGopayOrderId());
				if(oldLog == null){
					PayRouteLog payLog = new PayRouteLog();
					payLog.setGopayOrderId(mainOrder.getGopayOrderId());
					payLog.setType("0");
					if(common[2]!=null){
						payLog.setSolutionId(Long.valueOf(common[2].toString()));
					}
					if(common[4]!=null){
						payLog.setRuleId(Long.valueOf(common[4].toString()));
					}
					payLog.setPayReq(FastJsonUtil.toJSONString(map));
					payLog.setPayRep(FastJsonUtil.toJSONString(common));
					payLog.setCreatedTs(curDate);
					payRouteLogDAO.save(payLog);
				}
			}catch (Exception e){
				logger.error("付款单：{},保存付款路由日志异常",mainOrder.getGopayOrderId(),e);
			}
			return banks;
		}
		return null;
	}

	/**
	 * 判断商户是否个性化
	 * @param custId
	 * @return
	 */
	public Boolean isSpecialRoute(String custId) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("custId",custId);
		StringBuilder sb = new StringBuilder();
		sb.append(" SELECT rsc.cust_id,rsc.solution_id  FROM pay_spec_route_solution rs ");
		sb.append(" left join pay_spec_route_solu_cust_rel rsc  on rs.solution_id = rsc.solution_id ");
		sb.append(" where rs.del_flag = '2' and rsc.del_flag = '2' and rs.is_valid = '1' and rsc.cust_id =:custId");
		List<Object[]> rsList = commonQueryDAO.findBySql(sb.toString(),map);
		if (rsList != null && rsList.size() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * 获取网联出款通道银行代码
	 * @param receiveBankCode
	 * @return
	 */
	public String chooseNuccPayChannel(String receiveBankCode) {
		StringBuilder sb = new StringBuilder();
		sb.append(" select t.cur_bank_code from pay_nucc_channel t where t.rec_bank_code =:receiveBankCode");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("receiveBankCode", receiveBankCode);
		List<Object[]> rsList = commonQueryDAO.findBySql(sb.toString(), map);
		if (rsList != null && rsList.size() > 0) {
			return String.valueOf(rsList.get(0));
		}else{
			//出款通道里面没有配置的银行全部走工行出款
			return BankConstants.BANK_LOGO_ICBC;
		}
	}
}
